from tkinter.messagebox import NO
import exifread
import os
import datetime
import sys
import shutil
import hashlib
import piexif
from PIL import Image
from sys import argv


# 计算文件hash值
def calc_hash(file_path):
    file = open(file_path, 'rb')            # 打开文件
    sha1obj = hashlib.sha1()                # 获取sha1计算对象
    sha1obj.update(file.read(100*1024))     # 读取前100kB计算hash值,如果不足100kb也不会出错
    hash1 = sha1obj.hexdigest()             # 转化为16进制可读字符
    file.close()                            # 关闭文件
    return hash1                            # 返回hash值字符串


# 将文件移动到特定文件夹,文件名相同
def move_file(src_file, dst_folder):
    if os.path.isfile(src_file):                                            # 源文件是否为文件
        src_name = os.path.split(src_file)[1]                               # 读取源文件名，去掉路径，只保留名字
        if not os.path.exists(dst_folder):                                  # 查看目的文件夹是否存在
            os.makedirs(dst_folder)                                         # 不存在就建立文件夹
        if os.path.isdir(dst_folder):
            shutil.move(src_file, os.path.join(dst_folder, src_name))       # 移动文件


# 找到所有扩展名的文件,并返回列表
def list_img_files(folder, extends):
    _files = []  # 用于保存文件列表
    if os.path.isdir(folder):  # 目录
        _list = os.listdir(folder)
    else:  # 单个文件
        _list = [os.path.split(folder)[1]]
        folder = os.path.split(folder)[0]

    for i in _list:
        path = os.path.join(folder, i)  # 将文件名连接成完整路径

        if os.path.isdir(path):  # 递归访问子目录
            _files.extend(list_img_files(path, extends))

        if os.path.isfile(path):  # 过滤扩展名后加入列表返回
            if path.split('.')[-1].upper() in extends:
                _files.append(path)
    print("一共找到%d个文件" % len(_files))
    _files.sort()
    return _files


# 读取exif中文件名,并返回字符串
def get_date_from_exif(file_path):
    tag = 'EXIF DateTimeOriginal'
    date_format = '%Y:%m:%d %H:%M:%S'
    dt = None

    try:
        file = open(file_path, 'rb')
        tags = exifread.process_file(file)
        file.close()
        if tag in tags:
            try:
                dt = datetime.datetime.strptime(str(tags[tag])[:19], date_format)
            except ValueError:
                print('日期格式错误 -> %s' % file_path)
                return None
        return dt

    except IOError:
        print('文件读取错误 -> %s' % file_path)
        return None


# 读取文件系统中的时间,返回字符串
def get_date_from_file_system(file_path):
    time1 = os.stat(file_path).st_ctime  # last change
    time2 = os.stat(file_path).st_mtime  # last modify
    dt = datetime.datetime.fromtimestamp(min(time1, time2))
    # save_to_exif(file_path, dt)
    return dt

# 读取文件时间先从exif中读,再从系统中读
def get_date_from_exif_or_system(file_path):
    dt = get_date_from_exif(file_path)
    if  dt == None :
        print("无Exif信息,读文件修改时间:%s" % file_path)
        dt = get_date_from_file_system(file_path)
    return dt 

# 读取文件名,解析成字符串
def get_date_from_filename(file_path, date_format):
    try:
        path, name = os.path.split(file_path)
        name = name.split('.')[-2]
        dt = datetime.datetime.strptime(name, date_format)
    except ValueError:
        print('日期格式解析错误 -> %s' % file_path)
        return None

    # 保存exif日期信息到文件中
    # save_to_exif(file_path, dt)
    return dt


# 批量重命名
def rename(src_folder, start_num, file_type, date_string_function, date_format=None):
    output_format1 = '%Y%m%d-%H%M%S#'
    output_format2 = '%Y%m%d-%H%M%S#'
    output_format3 = '%Y%m%d-%H%M%S#'
    output_format4 = '%Y%m%d-%H%M%S#'

    n = start_num
    for j in list_img_files(src_folder, file_type):
        dir_name, file_name = os.path.split(j)
        extend = file_name.split('.')[-1]

        # 根据文件名重命名时,要加入日期解析格式
        if date_format is None:
            dt = date_string_function(j)
        else:
            dt = date_string_function(j, date_format)

        # 重命名文件
        if dt is not None:
            # 决定用何种连接符
            if date_string_function is get_date_from_exif:
                date_string = dt.strftime(output_format1)

            elif date_string_function is get_date_from_filename:
                date_string = dt.strftime(output_format2)
                if get_date_from_exif(j) is not None:
                    print('存在Exif信息,跳过从文件名读取 -> %s' % j)
                    continue

            elif date_string_function is get_date_from_file_system:
                date_string = dt.strftime(output_format3)
                if get_date_from_exif(j) is not None:
                    print('存在Exif信息,跳过从系统读取 -> %s' % j)
                    continue
            else:
                date_string = dt.strftime(output_format4)

            os.rename(j, os.path.join(dir_name, date_string + '%05d.%s' % (n, extend.upper())))
        else:
            print('文件重命名失败,找不到日期 -> %s' % j)
            continue

        n = n + 1
        print('处理%05d个文件,当前序号%05d\r' % (n - start_num, n), end='')
    print('处理%05d个文件,当前序号%05d\r' % (n - start_num, n), end='')


def print_date(src_folder, file_type):
    date_tags = ['Image DateTime', 'GPS GPSDate', 'EXIF DateTimeOriginal', 'EXIF DateTimeDigitized']
    for file in list_img_files(src_folder, file_type):
        f = open(file, 'rb')
        tags = exifread.process_file(f)
        f.close()
        for tag_name in date_tags:
            if tag_name in tags:
                print(str(tags[tag_name]) + ' | ', end='')
            else:
                print('None | ', end='')
        print(file)


def find_dump(file_type, src, dst):
    hash_table = {}
    n = 0
    for j in list_img_files(src, file_type):
        path, file = os.path.split(j)   # 分离出源文件路径
        if path != dst:                 # 判断是否为已找到的重复文件
            tmp = calc_hash(j)          # 计算hash值
            if tmp in hash_table:       # 是否为重复hash值
                move_file(j, dst)       # 移动文件
                print('%s与%s(%s)是重复文件' % (hash_table[tmp], j, tmp))
            else:
                hash_table[tmp] = j     # 未重复文件加入hash表
            n = n + 1                   # 计数加1
            print('已处理: %d 个文件\r' % n, end='')

def find_dump2(file_type, src, cmp, dst):
    hash_table = {}
    n = 0
    # 先计算cmp中的所有文件的hash值
    for i in list_img_files(cmp, file_type):
        hash_table[calc_hash(i)] = i
        n = n + 1
        print('正在计算被比较的模板(不移走)文件夹中文件的Hash值: %d 个文件\r' % n, end='')
        
    # 将要处理的src文件夹中的文件一一比较
    n = 0
    for j in list_img_files(src, file_type):
        path, file = os.path.split(j)   # 分离出源文件路径
        if path != dst:                 # 判断是否为已找到的重复文件
            tmp = calc_hash(j)          # 计算hash值
            if tmp in hash_table:       # 是否为重复hash值
                move_file(j, dst)       # 移动文件
                print('要移走的%s与比较模板文件夹中%s(%s)重复' % (j, hash_table[tmp], tmp))
            else:
                hash_table[tmp] = j
                
            n = n + 1                   # 计数加1
            print('已处理: %d 个文件\r' % n, end='')
        else:
            print("要移动到的文件夹与源文件夹不能相同")
            
        

# 将日期信息保存到exif信息中
def save_to_exif(file_path, dt):
    date_format = '%Y:%m:%d %H:%M:%S'
    img = Image.open(file_path)

    exif_bytes = img.info['exif']
    exif_dict = piexif.load(exif_bytes)

    exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal] = dt.strftime(date_format)
    exif_dict['Exif'][piexif.ExifIFD.DateTimeDigitized] = dt.strftime(date_format)

    exif_bytes = piexif.dump(exif_dict)
    img.save(file_path, exif=exif_bytes, quality=95)


if __name__ == '__main__':
    if len(argv) < 4:
        print('使用示例:\n'
              '   用Exif信息为JPG和PNG文件重命名,起始序号为123\n'
              '   \tphoto.exe rename_exif JPG_PNG E:\\Photo 123\n'
              '   根据文件名中的日期为JPG文件重命名,原文件名日期格式为%Y-%m-%d %H%M%S,起始序号200\n'
              '   \tphoto.exe rename_filename JPG E:\\Photo "%Y-%m-%d %H%M%S" 200\n'
              '   根据文件系统中的修改时间,最后修改时间重命名JPG文件\n'
              '   \tphoto.exe rename_system JPG E:\\Photo 300\n'
              '   根据文件系统中EXIF和修改时间,最后修改时间重命名JPG文件\n'
              '   \tphoto.exe rename_exif_or_system JPG E:\\Photo 300\n'        
              '   打印E:\\Photo下所有日期相关的参数\n'
              '   \tphoto.exe print_date JPG_PNG E:\\Photo\n'
              '   找出在E:\\Photo下所有重复的文件转移到E:\\Dump文件夹中\n'
              '   \tphoto.exe find_dump JPG_PNG_TIFF E:\\Photo E:\\Dump\n'
              '   找出在E:\\First与E:\\Second是否重复,然后将E:\\First下所有重复的文件转移到E:\\Dump文件夹中\n'
              '   \tphoto.exe find_dump2 JPG_PNG_TIFF E:\\First(被移走) E:\\Second E:\\Dump\n')
        sys.exit()

    cmd_ = argv[1]
    file_type_ = str(argv[2]).upper().split('_')
    folder_ = argv[3]

    if cmd_ == 'rename_exif':
        rename(folder_, int(argv[4]), file_type_, get_date_from_exif)
    elif cmd_ == 'rename_filename':
        rename(folder_, int(argv[5]), file_type_, get_date_from_filename, argv[4])
    elif cmd_ == 'rename_system':
        rename(folder_, int(argv[4]), file_type_, get_date_from_file_system)
    elif cmd_ == 'rename_exif_or_system':
        rename(folder_, int(argv[4]), file_type_, get_date_from_exif_or_system)
    elif cmd_ == 'print_date':
        print_date(folder_, file_type_)
    elif cmd_ == 'find_dump':
        find_dump(file_type_, folder_, argv[4])
    elif cmd_ == 'find_dump2':
        find_dump2(file_type_, folder_, argv[4], argv[5])
        
